using System;
using System.Collections.Generic;

namespace CLELCore.Assembly.CLELInstructions
{
	public static class CLELInstructionConvertor
	{
		public static List<CLELInstruction> ToCLELInstructions(byte [] code,bool little_endian)
		{
			if(code == null)
				return null;
			List<CLELInstruction> ret = new List<CLELInstruction>();
			int i;
			int code_size = code.Length;
			CLELInstruction inst;
			for(i = 0;i < code_size;)
			{
				inst = ReadCLELInstruction(code,i,little_endian);
				ret.Add(inst);
				i += inst.Length;
			}

			return ret;
		}
		
		public static byte[] ToBytes(List<CLELInstruction> code,bool little_endian)
		{
			if(code == null)
				return null;
			byte [] ret;
			byte [] inst;
			int total_size = 0;
			int i,j,count = code.Count;
			for(i = 0;i < count;i++)
			{
				total_size += code[i].Length;
			}
			ret = new byte[total_size];
			
			int k = 0;
			int inst_count;
			for(i = 0;i < count;i++)
			{
				inst = code[i].GetBytes(little_endian);
				inst_count = inst.Length;
				for(j = 0;j < inst_count;j++)
				{
					ret[k] = inst[j];
					k++;
				}
			}
	
			return ret;
		}
		
		private static CLELInstruction ReadCLELInstruction(byte[] buffer,int off,bool little_endian)
		{
			CLELInstruction inst = null;
			byte opcode = buffer[off];
			switch(opcode)
			{
				case CLELOpcode.NOP:		inst = new Nop();
								break;
				case CLELOpcode.BREAK:		inst = new Break();
								break;
				case CLELOpcode.LDARG0:		inst = new Ldarg0();
								break;
				case CLELOpcode.LDARG1:		inst = new Ldarg1();
								break;
				case CLELOpcode.LDARG2:		inst = new Ldarg2();
								break;
				case CLELOpcode.LDARG3:		inst = new Ldarg3();
								break;
				case CLELOpcode.LDLOC0:		inst = new Ldloc0();
								break;
				case CLELOpcode.LDLOC1:		inst = new Ldloc1();
								break;
				case CLELOpcode.LDLOC2:		inst = new Ldloc2();
								break;
				case CLELOpcode.LDLOC3:		inst = new Ldloc3();
								break;
				case CLELOpcode.STLOC0:		inst = new Stloc0();
								break;
				case CLELOpcode.STLOC1:		inst = new Stloc1();
								break;
				case CLELOpcode.STLOC2:		inst = new Stloc2();
								break;
				case CLELOpcode.STLOC3:		inst = new Stloc3();
								break;
				case CLELOpcode.LDARGS:		inst = new Ldargs(buffer[off+1]);
								break;
				case CLELOpcode.LDARGAS:	inst = new Ldargas(buffer[off+1]);
								break;
				case CLELOpcode.STARGS:		inst = new Stargs(buffer[off+1]);
								break;
				case CLELOpcode.LDLOCS:		inst = new Ldlocs(buffer[off+1]);
								break;
				case CLELOpcode.LDLOCAS:	inst = new Ldlocas(buffer[off+1]);
								break;
				case CLELOpcode.STLOCS:		inst = new Stlocs(buffer[off+1]);
								break;
				case CLELOpcode.LDNULL:		inst = new Ldnull();
								break;
				case CLELOpcode.LDCI4M1:	inst = new Ldci4m1();
								break;
				case CLELOpcode.LDCI40:		inst = new Ldci40();
								break;
				case CLELOpcode.LDCI41:		inst = new Ldci41();
								break;
				case CLELOpcode.LDCI42:		inst = new Ldci42();
								break;
				case CLELOpcode.LDCI43:		inst = new Ldci43();
								break;
				case CLELOpcode.LDCI44:		inst = new Ldci44();
								break;
				case CLELOpcode.LDCI45:		inst = new Ldci45();
								break;
				case CLELOpcode.LDCI46:		inst = new Ldci46();
								break;
				case CLELOpcode.LDCI47:		inst = new Ldci47();
								break;
				case CLELOpcode.LDCI48:		inst = new Ldci48();
								break;
				case CLELOpcode.LDCI4S:		inst = new Ldci4s(buffer[off+1]);
								break;
				case CLELOpcode.LDCI4:		inst = new Ldci4(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDCI8:		inst = new Ldci8(Bits.ReadLong(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDCR4:		inst = new Ldcr4(Bits.ReadFloat(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDCR8:		inst = new Ldcr8(Bits.ReadDouble(buffer,off+1,little_endian));
								break;
				case CLELOpcode.DUP:		inst = new Dup();
								break;
				case CLELOpcode.POP:		inst = new Pop();
								break;
				case CLELOpcode.JMP:		inst = new Jmp(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CALL:		inst = new Call(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CALLI:		inst = new Calli(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.RET:		inst = new Ret();
								break;
				case CLELOpcode.BRS:		inst = new Brs((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BRFALSES:	inst = new Brfalses((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BRTRUES:	inst = new Brtrues((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BEQS:		inst = new Beqs((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BGES:		inst = new Bges((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BGTS:		inst = new Bgts((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BLES:		inst = new Bles((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BLTS:		inst = new Blts((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BNEUNS:		inst = new Bneuns((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BGEUNS:		inst = new Bgeuns((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BGTUNS:		inst = new Bgtuns((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BLEUNS:		inst = new Bleuns((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BLTUNS:		inst = new Bltuns((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.BR:		inst = new Br(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BRFALSE:	inst = new Brfalse(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BRTRUE:		inst = new Brtrue(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BEQ:		inst = new Beq(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BGE:		inst = new Bge(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BGT:		inst = new Bgt(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BLE:		inst = new Ble(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BLT:		inst = new Blt(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BNEUN:		inst = new Bneun(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BGEUN:		inst = new Bgeun(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BGTUN:		inst = new Bgtun(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BLEUN:		inst = new Bleun(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.BLTUN:		inst = new Bltun(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.SWITCH:		inst = ParseSwitch(buffer,off,little_endian);
								break;
				case CLELOpcode.LDINDI1:	inst = new Ldindi1();
								break;
				case CLELOpcode.LDINDU1:	inst = new Ldindu1();
								break;
				case CLELOpcode.LDINDI2:	inst = new Ldindi2();
								break;
				case CLELOpcode.LDINDU2:	inst = new Ldindu2();
								break;
				case CLELOpcode.LDINDI4:	inst = new Ldindi4();
								break;
				case CLELOpcode.LDINDU4:	inst = new Ldindu4();
								break;
				case CLELOpcode.LDINDI8:	inst = new Ldindi8();
								break;
				case CLELOpcode.LDINDI:		inst = new Ldindi();
								break;
				case CLELOpcode.LDINDR4:	inst = new Ldindr4();
								break;
				case CLELOpcode.LDINDR8:	inst = new Ldindr8();
								break;
				case CLELOpcode.LDINDREF:	inst = new Ldindref();
								break;
				case CLELOpcode.STINDREF:	inst = new Stindref();
								break;
				case CLELOpcode.STINDI1:	inst = new Stindi1();
								break;
				case CLELOpcode.STINDI2:	inst = new Stindi2();
								break;
				case CLELOpcode.STINDI4:	inst = new Stindi4();
								break;
				case CLELOpcode.STINDI8:	inst = new Stindi8();
								break;
				case CLELOpcode.STINDR4:	inst = new Stindr4();
								break;
				case CLELOpcode.STINDR8:	inst = new Stindr8();
								break;
				case CLELOpcode.ADD:		inst = new Add();
								break;
				case CLELOpcode.SUB:		inst = new Sub();
								break;
				case CLELOpcode.MUL:		inst = new Mul();
								break;
				case CLELOpcode.DIV:		inst = new Div();
								break;
				case CLELOpcode.DIVUN:		inst = new Divun();
								break;
				case CLELOpcode.REM:		inst = new Rem();
								break;
				case CLELOpcode.REMUN:		inst = new Remun();
								break;
				case CLELOpcode.AND:		inst = new And();
								break;
				case CLELOpcode.OR:		inst = new Or();
								break;
				case CLELOpcode.XOR:		inst = new Xor();
								break;
				case CLELOpcode.SHL:		inst = new Shl();
								break;
				case CLELOpcode.SHR:		inst = new Shr();
								break;
				case CLELOpcode.SHRUN:		inst = new Shrun();
								break;
				case CLELOpcode.NEG:		inst = new Neg();
								break;
				case CLELOpcode.NOT:		inst = new Not();
								break;
				case CLELOpcode.CONVI1:		inst = new Convi1();
								break;
				case CLELOpcode.CONVI2:		inst = new Convi2();
								break;
				case CLELOpcode.CONVI4:		inst = new Convi4();
								break;
				case CLELOpcode.CONVI8:		inst = new Convi8();
								break;
				case CLELOpcode.CONVR4:		inst = new Convr4();
								break;
				case CLELOpcode.CONVR8:		inst = new Convr8();
								break;
				case CLELOpcode.CONVU4:		inst = new Convu4();
								break;
				case CLELOpcode.CONVU8:		inst = new Convu8();
								break;
				case CLELOpcode.CALLVIRT:	inst = new Callvirt(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CPOBJ:		inst = new Cpobj(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDOBJ:		inst = new Ldobj(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDSTR:		inst = new Ldstr(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.NEWOBJ:		inst = new Newobj(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CASTCLASS:	inst = new Castclass(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.ISINST:		inst = new Isinst(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CONVRUN:	inst = new Convrun();
								break;
				case CLELOpcode.UNBOX:		inst = new Unbox(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.THROW:		inst = new Throw();
								break;
				case CLELOpcode.LDFLD:		inst = new Ldfld(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDFLDA:		inst = new Ldflda(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.STFLD:		inst = new Stfld(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDSFLD:		inst = new Ldsfld(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDSFLDA:	inst = new Ldsflda(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.STSFLD:		inst = new Stsfld(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.STOBJ:		inst = new Stobj(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CONVOVFI1UN:	inst = new Convovfi1un();
								break;
				case CLELOpcode.CONVOVFI2UN:	inst = new Convovfi2un();
								break;
				case CLELOpcode.CONVOVFI4UN:	inst = new Convovfi4un();
								break;
				case CLELOpcode.CONVOVFI8UN:	inst = new Convovfi8un();
								break;
				case CLELOpcode.CONVOVFU1UN:	inst = new Convovfu1un();
								break;
				case CLELOpcode.CONVOVFU2UN:	inst = new Convovfu2un();
								break;
				case CLELOpcode.CONVOVFU4UN:	inst = new Convovfu4un();
								break;
				case CLELOpcode.CONVOVFU8UN:	inst = new Convovfu8un();
								break;
				case CLELOpcode.CONVOVFIUN:	inst = new Convovfiun();
								break;
				case CLELOpcode.CONVOVFUUN:	inst = new Convovfuun();
								break;
				case CLELOpcode.BOX:		inst = new Box(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.NEWARR:		inst = new Newarr(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDLEN:		inst = new Ldlen();
								break;
				case CLELOpcode.LDELEMA:	inst = new Ldelema(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDELEMI1:	inst = new Ldelemi1();
								break;
				case CLELOpcode.LDELEMU1:	inst = new Ldelemu1();
								break;
				case CLELOpcode.LDELEMI2:	inst = new Ldelemi2();
								break;
				case CLELOpcode.LDELEMU2:	inst = new Ldelemu2();
								break;
				case CLELOpcode.LDELEMI4:	inst = new Ldelemi4();
								break;
				case CLELOpcode.LDELEMU4:	inst = new Ldelemu4();
								break;
				case CLELOpcode.LDELEMI8:	inst = new Ldelemi8();
								break;
				case CLELOpcode.LDELEMI:	inst = new Ldelemi();
								break;
				case CLELOpcode.LDELEMR4:	inst = new Ldelemr4();
								break;
				case CLELOpcode.LDELEMR8:	inst = new Ldelemr8();
								break;
				case CLELOpcode.LDELEMREF:	inst = new Ldelemref();
								break;
				case CLELOpcode.STELEMI:	inst = new Stelemi();
								break;
				case CLELOpcode.STELEMI1:	inst = new Stelemi1();
								break;
				case CLELOpcode.STELEMI2:	inst = new Stelemi2();
								break;
				case CLELOpcode.STELEMI4:	inst = new Stelemi4();
								break;
				case CLELOpcode.STELEMI8:	inst = new Stelemi8();
								break;
				case CLELOpcode.STELEMR4:	inst = new Stelemr4();
								break;
				case CLELOpcode.STELEMR8:	inst = new Stelemr8();
								break;
				case CLELOpcode.STELEMREF:	inst = new Stelemref();
								break;
				case CLELOpcode.LDELEM:		inst = new Ldelem(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.STELEM:		inst = new Stelem(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.UNBOXANY:	inst = new Unboxany(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CONVOVFI1:	inst = new Convovfi1();
								break;
				case CLELOpcode.CONVOVFU1:	inst = new Convovfu1();
								break;
				case CLELOpcode.CONVOVFI2:	inst = new Convovfi2();
								break;
				case CLELOpcode.CONVOVFU2:	inst = new Convovfu2();
								break;
				case CLELOpcode.CONVOVFI4:	inst = new Convovfi4();
								break;
				case CLELOpcode.CONVOVFU4:	inst = new Convovfu4();
								break;
				case CLELOpcode.CONVOVFI8:	inst = new Convovfi8();
								break;
				case CLELOpcode.CONVOVFU8:	inst = new Convovfu8();
								break;
				case CLELOpcode.REFANYVAL:	inst = new Refanyval(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CKFINITE:	inst = new Ckfinite();
								break;
				case CLELOpcode.MKREFANY:	inst = new Mkrefany(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDTOKEN:	inst = new Ldtoken(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CONVU2:		inst = new Convu2();
								break;
				case CLELOpcode.CONVU1:		inst = new Convu1();
								break;
				case CLELOpcode.CONVI:		inst = new Convi();
								break;
				case CLELOpcode.CONVOVFI:	inst = new Convovfi();
								break;
				case CLELOpcode.CONVOVFU:	inst = new Convovfu();
								break;
				case CLELOpcode.ADDOVF:		inst = new Addovf();
								break;
				case CLELOpcode.ADDOVFUN:	inst = new Addovfun();
								break;
				case CLELOpcode.MULOVF:		inst = new Mulovf();
								break;
				case CLELOpcode.MULOVFUN:	inst = new Mulovfun();
								break;
				case CLELOpcode.SUBOVF:		inst = new Subovf();
								break;
				case CLELOpcode.SUBOVFUN:	inst = new Subovfun();
								break;
				case CLELOpcode.ENDFINALLY:	inst = new Endfinally();
								break;
				case CLELOpcode.LEAVE:		inst = new Leave(Bits.ReadInt(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LEAVES:		inst = new Leaves((sbyte)buffer[off+1]);
								break;
				case CLELOpcode.STINDI:		inst = new Stindi();
								break;
				case CLELOpcode.CONVU:		inst = new Convu();
								break;
				case CLELOpcode.OBJECTMODEL:	inst = ReadObjectModelCLELInstruction(buffer,off+1,little_endian);
								break;
				default:
								throw new CLELParserException("Error invalid optcode: " + opcode + "  offset: "+off);
			}
			return inst;
		}
		
		private static CLELInstruction ReadObjectModelCLELInstruction(byte[] buffer,int off,bool little_endian)
		{
			byte fun = buffer[off];
			CLELInstruction inst = null;
			switch(fun)
			{
				case CLELOpcode.ARGLIST:	inst = new Arglist();
								break;
				case CLELOpcode.CEQ:		inst = new Ceq();
								break;
				case CLELOpcode.CGT:		inst = new Cgt();
								break;
				case CLELOpcode.CGTUN:		inst = new Cgtun();
								break;
				case CLELOpcode.CLT:		inst = new Clt();
								break;
				case CLELOpcode.CLTUN:		inst = new Cltun();
								break;
				case CLELOpcode.LDFTN:		inst = new Ldftn(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDVIRTFTN:	inst = new Ldvirtftn(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDARG:		inst = new Ldarg(Bits.ReadUnsignedShort(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDARGA:		inst = new Ldarga(Bits.ReadUnsignedShort(buffer,off+1,little_endian));
								break;
				case CLELOpcode.STARG:		inst = new Starg(Bits.ReadUnsignedShort(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDLOC:		inst = new Ldloc(Bits.ReadUnsignedShort(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LDLOCA:		inst = new Ldloca(Bits.ReadUnsignedShort(buffer,off+1,little_endian));
								break;
				case CLELOpcode.STLOC:		inst = new Stloc(Bits.ReadUnsignedShort(buffer,off+1,little_endian));
								break;
				case CLELOpcode.LOCALLOC:	inst = new Localloc();
								break;
				case CLELOpcode.ENDFILTER:	inst = new Endfilter();
								break;
				case CLELOpcode.UNALIGNED:	inst = new Unaligned(buffer[off+1]);
								break;
				case CLELOpcode.VOLATILE:	inst = new Volatile();
								break;
				case CLELOpcode.TAIL:		inst = new Tail();
								break;
				case CLELOpcode.INITOBJ:	inst = new Initobj(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CONSTRAINED:	inst = new Constrained(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.CPBLK:		inst = new Cpblk();
								break;
				case CLELOpcode.INITBLK:	inst = new Initblk();
								break;
				case CLELOpcode.RETHROW:	inst = new Rethrow();
								break;
				case CLELOpcode.SIZEOF:		inst = new Sizeof(Bits.ReadToken(buffer,off+1,little_endian));
								break;
				case CLELOpcode.REFANYTYPE:	inst = new Refanytype();
								break;
				default:
								throw new CLELParserException("Error invalid object model function: " + fun);
			}
			return inst;
		}
	
		private static CLELInstruction ParseSwitch(byte[] buffer,int off,bool little_endian)
		{
			int num = Bits.ReadInt(buffer,off+1,little_endian);
			int i;
			List<Int32> parms = new List<Int32>();
			int next = off + 5;
			for(i = 0;i < num;i++)
			{
				parms.Add(Bits.ReadInt(buffer,next,little_endian));
				next += 4;
			}
			return new Switch(parms);
		}
	}
}
